package com.sz.framework.plugin;

import com.sz.framework.SzManager;
import com.sz.framework.exception.SzPluginException;
import com.sz.framework.function.hooks.SzPluginHookFunction;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * 插件管理中心，管理所有插件的加载与卸载
 *
 * @author: yxl
 * 2025-07-22
 */
public class SzPluginManager {

    public static SzPluginManager instance = new SzPluginManager();

    /**
     * 插件是否已经安装
     */
    public boolean isLoaded = false;

    /**
     * SPI文件所在的目录名称
     */
    public String spiPath = "sz";

    /**
     * 所有插件的集合
     */
    private final List<SzPlugin> plugins = new ArrayList<>();

    /**
     * 插件Install前置钩子
     */
    private final List<SzPluginHookModel<? extends SzPlugin>> beforeInstallHooks = new ArrayList<>();

    /**
     * 插件Install钩子
     */
    private final List<SzPluginHookModel<? extends SzPlugin>> installHooks = new ArrayList<>();

    /**
     * 插件Install后置钩子
     */
    private final List<SzPluginHookModel<? extends SzPlugin>> afterInstallHooks = new ArrayList<>();

    /**
     * 注册指定插件的Install前置钩子
     *
     * @param listenerClass   /
     * @param executeFunction /
     * @param <T>             /
     */
    public synchronized <T extends SzPlugin> SzPluginManager onBeforeInstall(Class<T> listenerClass, SzPluginHookFunction<T> executeFunction) {
        // 如果指定的插件已经安装完毕，则不再允许注册前置钩子函数
        SzPluginException.notTrue(isInstalledPlugin(listenerClass), "插件 [ " + listenerClass.getCanonicalName() + " ] 已安装完毕，不允许再注册 Install 前置钩子函数");
        beforeInstallHooks.add(new SzPluginHookModel<T>(listenerClass, executeFunction));
        return this;
    }

    /**
     * 注册指定插件的Install钩子
     *
     * @param listenerClass   /
     * @param executeFunction /
     * @param <T>             /
     */
    public synchronized <T extends SzPlugin> SzPluginManager onInstall(Class<T> listenerClass, SzPluginHookFunction<T> executeFunction) {
        // 如果指定的插件已经安装完毕，则不再允许注册 Install 钩子函数
        SzPluginException.notTrue(isInstalledPlugin(listenerClass), "插件 [ " + listenerClass.getCanonicalName() + " ] 已安装完毕，不允许再注册 Install 钩子函数");
        installHooks.add(new SzPluginHookModel<T>(listenerClass, executeFunction));
        return this;
    }

    /**
     * 注册指定插件的Install后置钩子
     *
     * @param listenerClass   /
     * @param executeFunction /
     * @param <T>             /
     */
    public synchronized <T extends SzPlugin> SzPluginManager onAfterInstall(Class<T> listenerClass, SzPluginHookFunction<T> executeFunction) {
        // 如果指定的插件已经安装完毕，则立即执行该钩子函数
        if (isInstalledPlugin(listenerClass)) {
            executeFunction.execute(getPlugin(listenerClass));
            return this;
        }
        afterInstallHooks.add(new SzPluginHookModel<T>(listenerClass, executeFunction));
        return this;
    }


    /**
     * 插件Destroy前置钩子
     */
    private final List<SzPluginHookModel<? extends SzPlugin>> beforeDestroyHooks = new ArrayList<>();

    /**
     * 插件Destroy钩子
     */
    private final List<SzPluginHookModel<? extends SzPlugin>> destroyHooks = new ArrayList<>();

    /**
     * 插件Destroy后置钩子
     */
    private final List<SzPluginHookModel<? extends SzPlugin>> afterDestroyHooks = new ArrayList<>();

    /**
     * 注册指定插件的Destroy前置钩子
     *
     * @param listenerClass   /
     * @param executeFunction /
     * @param <T>             /
     */
    public synchronized <T extends SzPlugin> SzPluginManager onBeforeDestroy(Class<T> listenerClass, SzPluginHookFunction<T> executeFunction) {
        beforeDestroyHooks.add(new SzPluginHookModel<T>(listenerClass, executeFunction));
        return this;
    }

    /**
     * 注册指定插件的Destroy钩子
     *
     * @param listenerClass   /
     * @param executeFunction /
     * @param <T>             /
     */
    public synchronized <T extends SzPlugin> SzPluginManager onDestroy(Class<T> listenerClass, SzPluginHookFunction<T> executeFunction) {
        destroyHooks.add(new SzPluginHookModel<T>(listenerClass, executeFunction));
        return this;
    }

    /**
     * 注册指定插件的Destroy后置钩子
     *
     * @param listenerClass   /
     * @param executeFunction /
     * @param <T>             /
     */
    public synchronized <T extends SzPlugin> SzPluginManager onAfterDestroy(Class<T> listenerClass, SzPluginHookFunction<T> executeFunction) {
        afterDestroyHooks.add(new SzPluginHookModel<T>(listenerClass, executeFunction));
        return this;
    }

    /**
     * 初始化加载所有插件
     */
    public synchronized void init() {
        if (isLoaded) {
            return;
        }
        loadPlugins();
        isLoaded = true;
    }


    /**
     * SPI加载所有插件
     * <p>
     * 加载jar包的 /META-INF/sz/ 目录下 com.sz.framework.plugin.SzPlugin 文件指定的实现类
     * </p>
     */
    public synchronized void loadPlugins() {
        SzManager.log.info("开始加载插件...");
        List<SzPlugin> plugins = loadPluginsBySpi(SzPlugin.class, spiPath);
        for (SzPlugin plugin : plugins) {
            instancePlugin(plugin);
        }
        SzManager.log.info("加载插件完成...");
    }

    public synchronized SzPluginManager instancePlugin(SzPlugin plugin) {
        SzPluginException.notEmpty(plugin, "插件不可为空");
        SzPluginException.notTrue(isInstalledPlugin(plugin.getClass()), "插件 [ " + plugin.getClass().getCanonicalName() + " ] 已安装，不可重复安装");
        // 执行插件的前置钩子函数
        consumeHooks(beforeInstallHooks, plugin.getClass());
        // 插件安装
        int consumeCount = consumeHooks(installHooks, plugin.getClass());
        if (consumeCount == 0) {
            plugin.install();
        }
        // 执行插件的后置钩子
        consumeHooks(afterInstallHooks, plugin.getClass());
        plugins.add(plugin);
        return this;
    }

    /**
     * 卸载指定插件
     *
     * @param plugin /
     */
    public synchronized SzPluginManager destroyPlugin(SzPlugin plugin) {
        SzPluginException.notEmpty(plugin, "插件不可为空");
        SzPluginException.notTrue(!isInstalledPlugin(plugin.getClass()), "插件 [ " + plugin.getClass().getCanonicalName() + " ] 未安装，无法卸载");
        // 执行该插件的 destroy 前置钩子
        consumeHooks(beforeDestroyHooks, plugin.getClass());
        // 插件卸载
        int consumeCount = consumeHooks(destroyHooks, plugin.getClass());
        if (consumeCount == 0) {
            plugin.uninstall();
        }
        // 执行该插件的 destroy 后置钩子
        consumeHooks(afterDestroyHooks, plugin.getClass());
        plugins.remove(plugin);
        return this;
    }

    /**
     * 消费指定集合的钩子函数，返回消费的数量
     *
     * @param hooks       /
     * @param pluginClass /
     * @param <T>         /
     */
    protected synchronized <T extends SzPlugin> int consumeHooks(List<SzPluginHookModel<? extends SzPlugin>> hooks, Class<T> pluginClass) {
        int consumeCount = 0;
        for (int i = 0; i < hooks.size(); i++) {
            SzPluginHookModel<? extends SzPlugin> model = hooks.get(i);
            if (model.listenerClass.equals(pluginClass)) {
                model.executeFunction.execute(getPlugin(pluginClass));
                hooks.remove(i);
                i--;
                consumeCount++;
            }
        }
        return consumeCount;
    }

    /**
     * 获取指定类型的插件
     *
     * @param pluginClass /
     * @param <T>         /
     * @return /
     */
    public synchronized <T extends SzPlugin> T getPlugin(Class<T> pluginClass) {
        for (SzPlugin plugin : plugins) {
            if (plugin.getClass().equals(pluginClass)) {
                return (T) plugin;
            }
        }
        return null;
    }

    /**
     * 判断是否已经安装了指定插件
     *
     * @param pluginClass
     * @return /
     */
    public synchronized <T extends SzPlugin> boolean isInstalledPlugin(Class<? extends SzPlugin> pluginClass) {
        for (SzPlugin plugin : plugins) {
            if (plugin.getClass().equals(pluginClass)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 自定义 SPI 读取策略 （无状态函数）
     *
     * @param serviceInterface SPI 接口
     * @param spiPath          目录名称
     * @param <T>              /
     * @return /
     */
    private <T> List<T> loadPluginsBySpi(Class<T> serviceInterface, String spiPath) {
        String path = "META-INF/" + spiPath + "/" + serviceInterface.getName();
        List<T> providers = new ArrayList<>();
        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            Enumeration<URL> resources = classLoader.getResources(path);
            while (resources.hasMoreElements()) {
                URL url = resources.nextElement();
                try (InputStream is = url.openStream()) {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                    String line;
                    while ((line = reader.readLine()) != null) {
                        line = line.trim();
                        // 忽略空行和注释行
                        if (!line.isEmpty() && !line.startsWith("#")) {
                            Class<?> clazz = Class.forName(line, true, classLoader);
                            T instance = serviceInterface.cast(clazz.getDeclaredConstructor().newInstance());
                            providers.add(instance);
                        }
                    }
                } catch (Exception e) {
                    throw new SzPluginException("SPI 插件加载失败: " + e.getMessage(), e);
                }
            }
        } catch (Exception e) {
            throw new SzPluginException("SPI 插件加载失败: " + e.getMessage(), e);
        }
        return providers;
    }


}
